using System;
using System.Collections.Generic;
using BitMango;
using BitMango.Diagnostics;
using Holoville.HOTween;
using UnityEngine;

public class BMSound : ScriptableObject
{
    public static BMSound Instance
    {
        get
        {
            if (BMSound.instance == null)
            {
                BMSound.instance = (Resources.Load("BMSoundSettings") as BMSound);
                if (BMSound.instance == null)
                {
                    BMSound.instance = ScriptableObject.CreateInstance<BMSound>();
                }
            }
            return BMSound.instance;
        }
    }

    public float VolumeOfBGM
    {
        get
        {
            return this.clipsForBGM[0].GetPlayer(0).volume;
        }
        set
        {
            for (int i = 0; i < this.clipsForBGM.Count; i++)
            {
                this.clipsForBGM[i].GetPlayer(0).volume = value;
            }
        }
    }

    private void OnEnable()
    {
        UnityEngine.Object.DontDestroyOnLoad(this);
    }

    public void Initialize()
    {
        if (this.audioClips == null)
        {
            return;
        }
        if (BMSound.soundParent == null)
        {
            BMSound.soundParent = new GameObject("+BMSound").transform;
            UnityEngine.Object.DontDestroyOnLoad(BMSound.soundParent);
        }
        foreach (AudioClip audioClip in this.audioClips)
        {
            if (audioClip != null)
            {
                this.clipDic.Add(audioClip.name, new BMSound.ClipInfo(audioClip, 0));
            }
        }
    }

    public void SetVolumeSFX(float volumeSFX)
    {
        foreach (BMSound.ClipInfo clipInfo in this.clipDic.Values)
        {
            if (this.clipsForBGM.IndexOf(clipInfo) == -1)
            {
                clipInfo.SetVolume(volumeSFX);
            }
        }
    }

    public void SetVolumeBGM(float volumeBGM)
    {
        for (int i = 0; i < this.clipsForBGM.Count; i++)
        {
            if (this.clipsForBGM[i] != null)
            {
                this.clipsForBGM[i].GetPlayer(0).volume = volumeBGM;
            }
        }
    }

    public void PlayBGM(string clipName, float volume = 1f, int channel = 0, bool loop = true, bool useFade = true)
    {
        while (this.clipsForBGM.Count <= channel)
        {
            this.clipsForBGM.Add(null);
        }
        BMSound.ClipInfo prevClip = this.clipsForBGM[channel];
        BMSound.ClipInfo curClip = this.FindClipInfoByName(clipName);
        if (curClip == null)
        {
            Debugger.LogError("BMSound.PlayBGM fail : clip not found " + clipName);
            return;
        }
        this.clipsForBGM[channel] = curClip;
        HOTween.Complete("BGM_Fade_" + channel);
        if (useFade)
        {
            Sequence sequence = new Sequence(new SequenceParms().Id("BGM_Fade_" + channel));
            if (prevClip != null && prevClip.IsPlaying)
            {
                sequence.Append(HOTween.To(prevClip.GetPlayer(0), 0.5f, "volume", 0f));
                sequence.AppendInterval(0.3f);
            }
            sequence.AppendCallback(delegate ()
            {
                curClip.Play(volume, loop, 1f);
            });
            sequence.Append(HOTween.To(curClip.GetPlayer(0), 0.5f, "volume", volume));
            if (prevClip != null && prevClip.IsPlaying)
            {
                sequence.AppendCallback(delegate ()
                {
                    prevClip.Stop();
                });
            }
            sequence.Play();
        }
        else
        {
            if (prevClip != null)
            {
                prevClip.Stop();
            }
            curClip.Play(volume, loop, 1f);
        }
    }

    public void StopBGM(int channel = 0, bool useFade = true, float fadeTime = 0.5f)
    {
        if (this.clipsForBGM.Count <= channel)
        {
            return;
        }
        BMSound.ClipInfo clip = this.clipsForBGM[channel];
        Debugger.Assert(clip != null);
        if (!clip.IsPlaying)
        {
            return;
        }
        HOTween.Complete("BGM_Fade_" + channel);
        if (useFade)
        {
            Sequence sequence = new Sequence(new SequenceParms().Id("BGM_Fade_" + channel));
            sequence.Append(HOTween.To(clip.GetPlayer(0), fadeTime, "volume", 0f));
            sequence.AppendCallback(delegate ()
            {
                clip.Stop();
            });
            sequence.Play();
        }
        else
        {
            clip.Stop();
        }
    }

    public void StopBGMAll(bool useFade = true)
    {
        for (int i = 0; i < this.clipsForBGM.Count; i++)
        {
            this.StopBGM(i, useFade, 0.5f);
        }
    }

    public void Play(string clipName, float volume = 1f, bool loop = false, float pitch = 1f)
    {
        BMSound.ClipInfo clipInfo = this.FindClipInfoByName(clipName);
        if (clipInfo == null)
        {
            return;
        }
        clipInfo.Play(volume, loop, pitch);
    }

    public void Stop(string clipName)
    {
        BMSound.ClipInfo clipInfo = this.FindClipInfoByName(clipName);
        if (clipInfo == null)
        {
            return;
        }
        clipInfo.Stop();
    }

    public void StopAll()
    {
        foreach (KeyValuePair<string, BMSound.ClipInfo> keyValuePair in this.clipDic)
        {
            keyValuePair.Value.Stop();
        }
    }

    public bool IsPlayingBGM(int channel = 0)
    {
        BMSound.ClipInfo clipInfo = this.clipsForBGM[channel];
        return clipInfo != null && clipInfo.IsPlaying;
    }

    public bool IsPlaying(string clipName)
    {
        BMSound.ClipInfo clipInfo = this.FindClipInfoByName(clipName);
        return clipInfo != null && clipInfo.IsPlaying;
    }

    public void SetMaxChannelForClip(string clipName, int maxChn)
    {
        BMSound.ClipInfo clipInfo = this.FindClipInfoByName(clipName);
        if (clipInfo == null)
        {
            return;
        }
        clipInfo.SetMaxChannel(maxChn);
    }

    public void SetPitch(string clipName, float amount, bool useFade = true)
    {
        BMSound.ClipInfo clipInfo = this.FindClipInfoByName(clipName);
        if (clipInfo == null)
        {
            return;
        }
        HOTween.Complete("SetPitch_" + clipName);
        if (useFade)
        {
            Sequence sequence = new Sequence(new SequenceParms().Id("SetPitch_" + clipName));
            sequence.Append(HOTween.To(clipInfo.GetPlayer(0), 0.5f, "pitch", amount));
            sequence.Play();
        }
        else
        {
            clipInfo.SetPitch(amount);
        }
    }

    private BMSound.ClipInfo FindClipInfoByName(string clipName)
    {
        if (this.clipDic.ContainsKey(clipName))
        {
            return this.clipDic[clipName];
        }
        Debugger.Assert(false, "Can't found audio clip - " + clipName);
        return null;
    }

    private const string themeSettingsAssetName = "BMSoundSettings";

    private const string themeSettingsPath = "Resources";

    private const string themeSettingsAssetExtension = ".asset";

    private static BMSound instance;

    [NonSerialized]
    public static Transform soundParent;

    [NonSerialized]
    private Dictionary<string, BMSound.ClipInfo> clipDic = new Dictionary<string, BMSound.ClipInfo>();

    private List<BMSound.ClipInfo> clipsForBGM = new List<BMSound.ClipInfo>();

    public AudioClip[] audioClips;

    public bool warnOnSkip = true;

    private class ClipInfo
    {
        public ClipInfo(AudioClip _clip, int max = 0)
        {
            this.clip = _clip;
            this.SetMaxChannel(max);
        }

        public bool IsPlaying
        {
            get
            {
                for (int i = 0; i < this.sources.Count; i++)
                {
                    if (this.sources[i].volume > 0f)
                    {
                        return true;
                    }
                }
                return false;
            }
        }

        public void SetMaxChannel(int max)
        {
            this.maxCnt = max;
            if (this.maxCnt > 0)
            {
                for (int i = this.sources.Count; i < max; i++)
                {
                    this.sources.Add(this.CreateAudioSource(this.clip.name + "_" + this.sources.Count));
                }
            }
        }

        public AudioSource GetPlayer(int idx = 0)
        {
            if (this.sources.Count <= idx)
            {
                for (int i = this.sources.Count; i < idx + 1; i++)
                {
                    this.sources.Add(this.CreateAudioSource(this.clip.name + "_" + this.sources.Count));
                }
            }
            return this.sources[idx];
        }

        public void SetVolume(float vol)
        {
            for (int i = 0; i < this.sources.Count; i++)
            {
                this.sources[i].volume = vol;
            }
        }

        private void PlayClip(AudioSource s, float vol, bool loop, float pitch = 1f)
        {
            s.clip = this.clip;
            s.loop = loop;
            s.volume = vol;
            s.pitch = pitch;
            s.Play();
        }

        public bool Play(float volume, bool loop, float pitch = 1f)
        {
            AudioSource audioSource = null;
            for (int i = 0; i < this.sources.Count; i++)
            {
                if (!this.sources[i].isPlaying)
                {
                    audioSource = this.sources[i];
                    break;
                }
            }
            if (audioSource == null)
            {
                if (this.maxCnt > 0)
                {
                    if (BMSound.instance.warnOnSkip)
                    {
                        UnityEngine.Debug.LogWarning(string.Concat(new object[]
                        {
                            "BMSoundHelper : ChannelCount(",
                            this.maxCnt,
                            ") for clip[",
                            this.clip.name,
                            "] exceeded. skipping.."
                        }));
                    }
                    return false;
                }
                audioSource = this.CreateAudioSource(this.clip.name + "_" + this.sources.Count);
                this.sources.Add(audioSource);
            }
            this.PlayClip(audioSource, volume, loop, pitch);
            return true;
        }

        public void Stop()
        {
            for (int i = 0; i < this.sources.Count; i++)
            {
                if (this.sources[i].isPlaying)
                {
                    this.sources[i].volume = 0f;
                    this.sources[i].Stop();
                }
            }
        }

        public void SetPitch(float amount)
        {
            for (int i = 0; i < this.sources.Count; i++)
            {
                this.sources[i].pitch = amount;
            }
        }

        private AudioSource CreateAudioSource(string name = null)
        {
            GameObject gameObject = new GameObject(name);
            AudioSource audioSource = gameObject.AddComponent<AudioSource>();
            audioSource.volume = 0f;
            gameObject.transform.parent = BMSound.soundParent;
            return audioSource;
        }

        public int maxCnt;

        public List<AudioSource> sources = new List<AudioSource>();

        public AudioClip clip;
    }
}
